package com.yanfan.common;

import io.netty.buffer.ByteBuf;
import org.apache.commons.lang3.StringUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;

/**
 * @author oliver foo
 * @description
 * @date 2022/3/15 14:44
 */
public class ByteUtil {
    public static final byte[] BIT_MASK = {(byte) 0x01, (byte) 0x02, (byte) 0x04, (byte) 0x08, (byte) 0x10, (byte) 0x20, (byte) 0x40, (byte) 0x80};


    public static byte[] getBytes(byte[] data, int pos, int length) {
        byte[] dest = new byte[length];
        System.arraycopy(data, pos, dest, 0, length);
        return dest;
    }

    public static boolean getBool(byte[] buffer, int pos, int bitPos) {
        int value = buffer[pos] & 0x0FF;
        return (value & BIT_MASK[bitPos]) != 0;
    }

    public static byte getInt8(byte[] buffer, int pos) {
        return buffer[pos];
    }

    public static short getUInt8(byte[] buffer, int pos) {
        return (short) (buffer[pos] & 0x0FF);
    }

    public static short getInt16(byte[] buffer, int pos) {
        int hi = (buffer[pos]);
        int lo = (buffer[pos + 1] & 0x00FF);
        return (short) ((hi << 8) + lo);
    }

    public static int getUInt16(byte[] buffer, int pos) {
        int hi = (buffer[pos] & 0x00FF);
        int lo = (buffer[pos + 1] & 0x00FF);
        return (hi << 8) + lo;
    }

    public static int getInt32(byte[] buffer, int pos) {
        return ((buffer[pos]) << 24 | (buffer[pos + 1] & 0xFF) << 16 | (buffer[pos + 2] & 0xFF) << 8 | (buffer[pos + 3] & 0xFF));
    }

    public static long getUInt32(byte[] buffer, int pos) {
        return ((long) (buffer[pos] & 0xFF) << 24 | (buffer[pos + 1] & 0xFF) << 16 | (buffer[pos + 2] & 0xFF) << 8 | (buffer[pos + 3] & 0xFF));
    }

    public static long getInt64(byte[] buffer, int pos) {
        return (((long) buffer[pos]) << 56 | ((long) buffer[pos + 1] & 0xFF) << 48 | ((long) buffer[pos + 2] & 0xFF) << 40 | ((long) buffer[pos + 3] & 0xFF) << 32 | ((long) buffer[pos + 4] & 0xFF) << 24 | ((long) buffer[pos + 5] & 0xFF) << 16 | ((long) buffer[pos + 6] & 0xFF) << 8 | ((long) buffer[pos + 7] & 0xFF));
    }

    public static float getFloat32(byte[] buffer, int pos) {
        int intFloat = getInt32(buffer, pos);
        return Float.intBitsToFloat(intFloat);
    }

    public static double getDouble64(byte[] buffer, int pos) {
        long intDouble = getInt64(buffer, pos);
        return Double.longBitsToDouble(intDouble);
    }


    public static byte[] convertToBytes(boolean[] bdata) {
        int byteCount = (bdata.length + 7) / 8;
        byte[] data = new byte[byteCount];
        for (int i = 0; i < bdata.length; i++) {
            data[i / 8] |= (bdata[i] ? 1 : 0) << (i % 8);
        }
        return data;
    }

    public static byte[] convertToBytes(boolean bdata) {
        return convertToBytes(new boolean[]{bdata});
    }

    public static byte[] convertToBytes(short sdata) {
        return convertToBytes(new short[]{sdata});
    }

    public static byte[] convertToBytes(int idata) {
        return convertToBytes(new int[]{idata});
    }

    public static byte[] convertToBytes(float fdata) {
        return convertToBytes(new float[]{fdata});
    }

    public static byte[] convertToBytes(long ldata) {
        return convertToBytes(new long[]{ldata});
    }

    public static byte[] convertToBytes(double ddata) {
        return convertToBytes(new double[]{ddata});
    }

    public static byte[] convertToBytes(short[] sdata) {
        int byteCount = sdata.length * 2;
        byte[] data = new byte[byteCount];
        for (int i = 0; i < sdata.length; i++) {
            data[i * 2] = (byte) (0xff & (sdata[i] >> 8));
            data[i * 2 + 1] = (byte) (0xff & sdata[i]);
        }
        return data;
    }

    public static byte[] convertToBytes(int[] idata) {
        int byteCount = idata.length * 4;
        byte[] data = new byte[byteCount];
        for (int i = 0; i < idata.length; i++) {
            data[i * 4] = (byte) (0xff & (idata[i] >> 24));
            data[i * 4 + 1] = (byte) (0xff & (idata[i] >> 16));
            data[i * 4 + 2] = (byte) (0xff & (idata[i] >> 8));
            data[i * 4 + 3] = (byte) (0xff & idata[i]);
        }
        return data;
    }

    public static byte[] convertToBytes(float[] fdata) {
        int byteCount = fdata.length * 4;
        byte[] data = new byte[byteCount];
        for (int i = 0; i < fdata.length; i++) {
            int datum = Float.floatToIntBits(fdata[i]);
            data[i * 4] = (byte) (0xff & (datum >> 24));
            data[i * 4 + 1] = (byte) (0xff & (datum >> 16));
            data[i * 4 + 2] = (byte) (0xff & (datum >> 8));
            data[i * 4 + 3] = (byte) (0xff & datum);
        }
        return data;
    }

    public static byte[] convertToBytes(long[] ldata) {
        int byteCount = ldata.length * 8;
        byte[] data = new byte[byteCount];
        for (int i = 0; i < ldata.length; i++) {
            data[i * 8] = (byte) (0xff & (ldata[i] >> 56));
            data[i * 8 + 1] = (byte) (0xff & (ldata[i] >> 48));
            data[i * 8 + 2] = (byte) (0xff & (ldata[i] >> 40));
            data[i * 8 + 3] = (byte) (0xff & (ldata[i] >> 32));
            data[i * 8 + 4] = (byte) (0xff & (ldata[i] >> 24));
            data[i * 8 + 5] = (byte) (0xff & (ldata[i] >> 16));
            data[i * 8 + 6] = (byte) (0xff & (ldata[i] >> 8));
            data[i * 8 + 7] = (byte) (0xff & ldata[i]);
        }
        return data;
    }

    public static byte[] convertToBytes(double[] ddata) {
        int byteCount = ddata.length * 8;
        byte[] data = new byte[byteCount];
        for (int i = 0; i < ddata.length; i++) {
            long datum = Double.doubleToLongBits(ddata[i]);
            data[i * 8] = (byte) (0xff & (datum >> 56));
            data[i * 8 + 1] = (byte) (0xff & (datum >> 48));
            data[i * 8 + 2] = (byte) (0xff & (datum >> 40));
            data[i * 8 + 3] = (byte) (0xff & (datum >> 32));
            data[i * 8 + 4] = (byte) (0xff & (datum >> 24));
            data[i * 8 + 5] = (byte) (0xff & (datum >> 16));
            data[i * 8 + 6] = (byte) (0xff & (datum >> 8));
            data[i * 8 + 7] = (byte) (0xff & datum);
        }
        return data;
    }


    public static boolean[] convertToBooleans(byte[] data) {
        boolean[] bdata = new boolean[data.length * 8];
        for (int i = 0; i < bdata.length; i++) {
            bdata[i] = ((data[i / 8] >> (i % 8)) & 0x1) == 1;
        }
        return bdata;
    }


    public static short[] convertToShorts(byte[] data) {
        short[] sdata = new short[data.length / 2];
        for (int i = 0; i < sdata.length; i++) {
            sdata[i] = toShort(data[i * 2], data[i * 2 + 1]);
        }
        return sdata;
    }

    public static double multiply(double d1, double d2) {
        try {
            return new BigDecimal(d1).multiply(new BigDecimal(d2)).setScale(4, RoundingMode.HALF_UP).doubleValue();
        } catch (Exception e) {
            return 0;
        }
    }

    public static double multiply(float d1, double d2) {
        try {
            return new BigDecimal(d1).multiply(new BigDecimal(d2)).setScale(4, RoundingMode.HALF_UP).doubleValue();
        } catch (Exception e) {
            return 0;
        }
    }

    public static double multiply(int d1, double d2) {
        try {
            return new BigDecimal(d1).multiply(new BigDecimal(d2)).setScale(4, RoundingMode.HALF_UP).doubleValue();
        } catch (Exception e) {
            return 0;
        }
    }

    public static double multiply(short d1, double d2) {
        try {
            return new BigDecimal(d1).multiply(new BigDecimal(d2)).setScale(4, RoundingMode.HALF_UP).doubleValue();
        } catch (Exception e) {
            return 0;
        }
    }

    public static double multiply(byte d1, double d2) {
        try {
            return new BigDecimal(d1).multiply(new BigDecimal(d2)).setScale(4, RoundingMode.HALF_UP).doubleValue();
        } catch (Exception e) {
            return 0;
        }
    }

    public static BigDecimal divide(double d1, double d2) {
        try {
            return new BigDecimal(d1).divide(new BigDecimal(d2), 4, RoundingMode.HALF_UP);
        } catch (Exception e) {
            return new BigDecimal(0);
        }
    }

    public static String dumpHex(short b) {
        return dumpHex(convertToBytes(b));
    }

    public static String dumpHex(byte b) {
        return "0x" + StringUtils.leftPad(Integer.toHexString(b & 0xff), 2, '0');
    }

    public static String dumpHex(byte[] b) {
        return dumpHex(b, 0, b.length);
    }


    public static String dumpHex(byte[] b, int pos, int len) {
        StringBuilder sb = new StringBuilder();
        for (int i = pos; i < len; i++) {
            sb.append(StringUtils.leftPad(Integer.toHexString(b[i] & 0xff), 2, '0'));
            sb.append(StringUtils.SPACE);
        }
        return sb.toString();
    }

    public static String dumpHex(ByteBuf buffer) {
        StringBuilder sb = new StringBuilder();
        for (int i = buffer.readerIndex(); i < buffer.writerIndex(); i++) {
            sb.append(StringUtils.leftPad(Integer.toHexString(buffer.getByte(i) & 0xff), 2, '0'));
            sb.append(StringUtils.SPACE);
        }
        return sb.toString();
    }


    public static short toShort(byte b1, byte b2) {
        return (short) ((b1 << 8) | (b2 & 0xff));
    }

}
